/*
*
*/
#include "utility.h"

#define MAX_CR 120
#define MIN_CR 40  /* Customers*/
#define MAX_TC 5  /* TicketClerks*/
#define MAX_CC 5  /* ConcessionClerks*/
#define MAX_TT 3  /* TicketTakers*/
#define MAX_MT 1  /* MovieTechnician*/
#define MAX_MR 1  /* Manager*/

#define true 1
#define false 0

#define MAX_GROUP 100  /*  Group */
#define MIN_GROUP 10  /*  Group */
#define MAX_GROUPSIZE 5  /*  groupsize */

#define MAX_CR_NUMBER 100  /*  Group */
#define MAX_GP_NUMBER 100  /*  Group */

#define MAX_VAR 200

#define RATE_POPCORN 0.75  /*  chance to buy Popcorn */
#define RATE_SODA 0.75  /*  chance to buy Soda */
#define RATE_BATHROOM 0.25  /*  chance to go bathroom */

#define PRICE_TICKET 12  /* ticket price */
#define PRICE_SODA 4     /* soda price */
#define PRICE_POPCORN 5  /* popcorn price */

#define FOOD_POPCORN 0
#define FOOD_SODA    1

#define MOVIE_PERIOD_BASE  200
#define MOVIE_PERIOD_VAR  100

#define MAX_SEAT   25  /*  capacity of theater room */
#define MAX_ROW    5   /*  theater room max row */
#define MAX_COL    5  /*  theater room max col */

/*struct define, struct for the ticketclerk*/
typedef  struct
{
    int isBusy;
    int isBreak;
    int id;
    /* group on service*/
    int groupId;
    /* customer in waiting line*/
    int waitingSize;
    int lock;
    /*private for the ticketclerk*/
    int ticketSum;
    /*private for concession clerk*/
    int food[2];
    /* amount in one group*/
    int amount;
    /* customer pay*/
    int payment;
    /* c0 is for customer in waiting line*/
    /* c1 is action between customer and employee*/
    int lineCondition;
    int msgCondition;
    char * employeeType;
} Employee;

typedef  struct
{
    int customerId;
    int groupId;
    int isTicketBuyer;
    int groupSize;
    /* how to find groupmate*/
    /*    Customer * customers;   /*Only head customers need this value*/
    int seatNumber;
} Customer;

int getIsBusy(Employee * employee)
{
    return employee->isBusy;
}
void setIsBusy(Employee * employee, int isBusy)
{
    employee->isBusy = isBusy;
}

int getIsBreak(Employee * employee)
{
    return employee->isBreak;
}
void setIsBreak(Employee * employee, int isBreak)
{
    employee->isBreak = isBreak;
}

int getWaitingSize(Employee * employee)
{
    return employee->waitingSize;
}

void addWaitingSize(Employee * employee)
{
    ++employee->waitingSize;
}
void subWaitingSize(Employee * employee)
{
    --employee->waitingSize;
}

int getAmount(Employee *employee)
{
    return employee->amount;
}
void setPayment(Employee *employee, int payment)
{
    employee->payment = payment;
}
void setTicketSum(Employee *employee, int ticketSum)
{
    employee->ticketSum = ticketSum;
}

void setGroupId(Employee *employee, int groupId)
{
    employee->groupId = groupId;
}
void setFood(Employee *employee, int index, int food)
{
    employee->food[index] = food;
}


int randNum;

int rand() {
    return (++randNum) % 10000;
}


int ticketClerkWorknoLock;
int ticketClerkWorkno;

int numGroups;
int nextCustomerId;
int numCustomers = 50;
int numCustomersInGroup[MAX_GROUP];
int headCustomerInGroup[MAX_GROUP];
int curCustomerNumInGroup[MAX_GROUP];

int groupAskForBathroom[MAX_GROUP];
int groupNeedBathroom[MAX_GROUP];
int numInGroupAskForBathroom[MAX_GROUP] ;

int groupLeaveTheater[MAX_GROUP];

int cutomerNoLock;
int lBuyTickets=-1;
int cNoTicketClerk=-1;
int tcNumber;
int noTicketClerk=-1;
int cBreakTicketClerk = -1;
int breakTicketClerk = 0;

int ticketClerkAmount[MAX_TC] = {0};
int ticketReceipt[MAX_TC] = {0};

int totalTicketSold;

int numGroupsRemaining;
/*lock define*/
int lAmount=-1;
int lTicketSold=-1;
/*end */

Customer cr[MAX_CR];
Employee tc[MAX_TC];
Employee cc[MAX_CC];
Employee tt[MAX_TT];
Employee mt;
Employee mr;
/* head customer and regular customer */
int lGroup[MAX_GROUP];
int cGroup[MAX_GROUP];
int groupTicket[MAX_GROUP] = {0};
int answerFood[MAX_GROUP] = {0};
int groupFood[MAX_GROUP] = {0};
int groupFoodSum[MAX_GROUP][2] = {0};
int groupCheck[MAX_GROUP] = {0};
int groupSeat[MAX_GROUP] = {0};
int groupLeaveRoom[MAX_GROUP] = {0};
int cGroupFood[MAX_GROUP];
int groupAskForFood[MAX_GROUP] = {0};
int lFindSeats;
int seatLocation[MAX_GROUPSIZE] = {0};
int seatPos = 0;

int answerBathroom[MAX_GROUP] = {0};
int cGroupBathroom[MAX_GROUP] = {0};


/*define for concession clerk*/
int noConcessionClerk = -1;
int cNoConcessionClerk = -1;
int cBreakConcessionClerk = -1;
int concessionClerkWorknoLock;
int lBuyFood=-1;
int ccNumber=-1;
int breakConcessionClerk = 0;

int concessionClerkWorkNO;
int concessionClerkAmount[MAX_CC];

/*define for tickettaker*/
int tickettakerWorkno;
int cNoTicketTaker = -1;
int noTicketTaker = -1;
int cBreakTicketTaker = -1;
int breakTicketTaker = 0;
int stopTicketTaken;
int stopTicketTakenArr[MAX_CC];

int  stopTicketTaken;
int ttNumber;
int ticketTaken;
int totalTicketTaken;

int    lCheckTickets;
int     cTicketTaken;
int     lTicketTaken;
int lCheckTickets;
int cTicketTaken;
int lTicketTaken;
int tickettakerWorknoLock;

/*defien for the movie technician*/

int    managerAskMTPlay;
int    currentCustomerSeated;
int    seatState[MAX_SEAT];

int    movieState;

int    lStopMovie;
int    lMovieState;
int    managerAskMTplaylock;
int    currentCustomerSeatedlock;
int currentCustomerAllSeated;
/*define for the use of manager*/
int   lCustomerLeft;
    
int    customerLeft;
int    totalAmount;

/**/

int createLock(char *s)
{
    return CreateLock(s, Strlen(s));
}

int createCV(char *s)
{
    return CreateCV(s, Strlen(s));
}


/*code for customer*/

/************************************************
 *
 *  start for customer
 *
 ************************************************/
/************************************************
 *
 *  start for head customer
 *
 ************************************************/

int getInLine(int lock, int cNoClerk, int count, int *noClerk, Employee employee[], Customer * customer )
{
    int i, j;
    int lineIndex = -1;
    int lineLen = -1;

    /* get in line lock to make sure no one can get in line at the same time */
    AcquireLock(lock);
    /* DEBUG('z', "\tGroup [%d] start find line\n", groupId) */;

    /* check every clerk */
    for (i = 0; i < count; ++i)
    {
        /*  if the clerk is not busy or not on break and no one wait in line, go to the line directly  */
        if (getIsBusy(&employee[i]) && !getIsBreak(&employee[i]) && getWaitingSize(&employee[i]) == 0)
        {
            lineIndex = i;
            break;
        }

        /*  if clerk is not on break, find a shortest line */
        if (!getIsBreak(&employee[i]) && (lineIndex == -1 || getWaitingSize(&employee[i]) < lineLen))
        {
            lineLen = getWaitingSize(&employee[i]);
            lineIndex = i;
        }
    }
    /* DEBUG('z', "\tGroup [%d] finish find line\n", groupId) */;

    if (lineIndex == -1)
    {
        /*  if all clerk are on break */
        /*  need to wait until the manager signal them */
        /* DEBUGINFO('c', "Customer [%d] in Group [%d] find noClerk concessionclerk, should wake up someone", customerId, groupId, employee[0].employeeType); */
        *noClerk = true;
        Printf("Customer [%d] in Group [%d] is in the lobby.\n", customer->customerId, customer->groupId);
        WaitCV(lock, cNoClerk);
        Printf("Customer [%d] in Group [%d] is leaving the lobby.\n",  customer->customerId, customer->groupId);
        return lineIndex;

    }
    else
    {
        /* if head customer find the shortest line, waiting in line customer number add 1         */
        /* DEBUG('z', "\tbusy%d\n", getIsBusy(employee[lineIndex])?1:0) */;
        addWaitingSize(&employee[lineIndex]);
    }
    /* DEBUG('z', "\t%dline%d\n", lineIndex, getWaitingSize(employee[lineIndex])) */;
    /* DEBUGINFO('c', "\tCustomer [%d] in Group [%d] get in concessionclerk line [%d], length:%d, busy:%d\n", customerId, groupId, getEmployeeType(employee[lineIndex]), lineIndex, getWaitingSize(employee[lineIndex]), getIsBusy(employee[lineIndex])?1:0); */

    return lineIndex;
}


/* Head customer buy ticket for group member */
void buyTickets(int customerId)
{
    int lineIndex;
    int i;
    int groupId;
    int amount;
    Customer  * currentCR;
    Employee * clerk;
    currentCR=&cr[customerId];
    groupId = currentCR->groupId;
buyTickets:
    lineIndex = -1;

    /*AcquireLock(lBuyTickets); */
    /* Head customer try to get in shortest line */
    while (lineIndex == -1)
    {
        lineIndex = getInLine(lBuyTickets, cNoTicketClerk, tcNumber, &noTicketClerk, tc, currentCR);
    }

    /*  get in which line */
    Printf("Customer [%d] in Group [%d] is getting in TicketClerk line [%d]\n",customerId,groupId,lineIndex);


    clerk = &tc[lineIndex];
    /*  if the clerk is busy serving other customer,wait for the clerk to wake them up  */
    /* DEBUGINFO('c', "Customer [%d] in Group [%d] get in %s line [%d], length:%d, busy:%d\n", customerId, groupId, getEmployeeType(clerk), lineIndex, getWaitingSize(clerk), getIsBusy(clerk)?1:0); */
    if (getIsBusy(clerk))
    {
        WaitCV(lBuyTickets, clerk->lineCondition);
    }
    Printf("Customer [%d] in Group [%d] is walking up to TicketClerk [%d] to buy [%d] tickets\n", currentCR->customerId, currentCR->groupId, lineIndex, currentCR->groupSize);

    /* DEBUGINFO('c', "Customer [%d] in group [%d] acquire lock [%s], owner %s", customerId, groupId, clerk.lock.getName(), clerk.lock.getOwnerThread()!=NULL?clerk.lock.getOwnerThread().getName():"NULL"); */


    /* DEBUGINFO('c', "Customer [%d] in Group [%d] sees TicketClerk [%d] break status [%d].\n",customerId,groupId,lineIndex, getIsBreak(clerk)?1:0); */
    /* waiting line number minus 1 */
    subWaitingSize(clerk);

    if (!getIsBreak(clerk))
    {
        /*  buy ticket :interact with TicketClerk */
        AcquireLock(clerk->lock);
        setIsBusy(clerk, true);
        ReleaseLock(lBuyTickets);
        /*  tell clerk tickets sum */
        setGroupId(clerk, currentCR->groupId);
        setTicketSum(clerk, currentCR->groupSize);
        SignalCV(clerk->lock, clerk->msgCondition);
        /* DEBUGINFO('c', "1 Customer [%d] in Group [%d] is walking up to TicketClerk [%d] to buy [%d] tickets\n",customerId,groupId,lineIndex,groupSize); */
        /*  wait for clerk to tell how much money */
        WaitCV(clerk->lock, clerk->msgCondition);
        /*  ask ticket clerk for total price */
        amount = getAmount(clerk);
        /*  pay money  */
        setPayment(clerk, amount);
        Printf("Customer [%d] in Group [%d] in TicketClerk line [%d] is paying [%d] for tickets\n",customerId,groupId,lineIndex,amount);
        /* DEBUGINFO('c', "2 Customer [%d] in Group [%d] in TicketClerk line [%d] is paying [%d] for tickets\n",customerId,groupId,lineIndex,amount); */
        /* to give the clerk money */
        SignalCV(clerk->lock, clerk->msgCondition);
        /*  wait to get tickets back */
        WaitCV(clerk->lock, clerk->msgCondition);
        /*  finish and leave */
        SignalCV(clerk->lock, clerk->msgCondition);
        /* DEBUGINFO('c', "3 Customer [%d] in Group [%d] is leaving TicketClerk [%d]\n",customerId,groupId,lineIndex); */
        Printf("Customer [%d] in Group [%d] is leaving TicketClerk[%d]\n",customerId,groupId,lineIndex);
    }
    else
    {
        /* if the clerk is on break,head customer need to start over and get another line */
        Printf("Customer [%d] in Group [%d] sees TicketClerk [%d] is on break.\n",customerId,groupId,lineIndex);
        ReleaseLock(lBuyTickets);
        goto buyTickets;
    }
    ReleaseLock(clerk->lock);
    /* DEBUGINFO('c', "Customer [%d] in group [%d] finish buy ticket from TicketClerk [%d]", customerId, groupId, lineIndex); */
}

/*  rand for food request */
int answerForFood(int food)
{
    return (rand()%4) > 0;
}

int countFood(int customerId, int groupId)
{
    int i;
    AcquireLock(lGroup[groupId]);
    /*  if head customer self want food */
    for (i = 0; i < 2; ++i)
    {
        groupFoodSum[groupId][i] += answerForFood(i);
    }
    ++answerFood[groupId];
    /*  ask group mate if they want food */
    BroadcastCV(lGroup[groupId], cGroupFood[groupId]);
    /*  set monitor variable true */
    groupAskForFood[groupId] = true;
    /*  wait for all the customer answer for food */
    if (answerFood[groupId] != cr[customerId].groupSize)
    {
        WaitCV(lGroup[groupId], cGroupFood[groupId]);
    }
    ReleaseLock(lGroup[groupId]);
    Printf("Customer [%d] in Group [%d] has [%d] popcorn and [%d] soda request from a group member\n",customerId,groupId,groupFoodSum[groupId][0],groupFoodSum[groupId][1]);

    /* if group member don't need any food */
    if (!groupFoodSum[groupId][0] && !groupFoodSum[groupId][1])
    {
        groupFood[groupId] = true;
        return false;
    }
    return true;
}

void buyFood(int customerId)
{
    int lineIndex;
    int i;
    int groupId;
    int amount;
    Customer  * currentCR;
    Employee * clerk;
    currentCR=&cr[customerId];
    groupId = currentCR->groupId;
buyFood:
    lineIndex = -1;
    /*AcquireLock(lBuyFood); */
    /* Head customer try to get in shortest line */
    while (lineIndex == -1)
    {
        lineIndex = getInLine(lBuyFood, cNoConcessionClerk, ccNumber, &noConcessionClerk, cc, currentCR);
    }
    /* DEBUGINFO('c', "Customer [%d] in group [%d] get in ConcessionClerk [%d]", customerId, groupId, lineIndex) */;
    Printf("Customer [%d] in Group [%d] is getting in ConcessionClerk line [%d]\n",customerId,groupId,lineIndex);

    clerk = &cc[lineIndex];

    /*  if the clerk is busy serving other customer,wait for the clerk to wake them up  */
    if (getIsBusy(clerk))
    {
        WaitCV(lBuyFood, clerk->lineCondition);
        /* DEBUGINFO('c', "Customer [%d] in group [%d] is wake up to goto ConcessionClerk [%d]", customerId, groupId, lineIndex) */;
    }
    /* DEBUGINFO('c', "Customer [%d] in group [%d] is walking up to ConcessionClerk [%d]", customerId, groupId, lineIndex) */;
    Printf("Customer [%d] in Group [%d] is walking up to ConcessionClerk[%d] to buy [%d] popcorn and [%d] soda.\n",customerId,groupId,lineIndex,groupFoodSum[groupId][0],groupFoodSum[groupId][1]);

    /* DEBUGINFO('c', "Customer [%d] in group [%d] acquire lock [%s], owner %s", customerId, groupId, clerk->lock->getName(), clerk->lock->getOwnerThread()!=NULL?clerk->lock->getOwnerThread()->getName():"NULL") */;
    /* DEBUGINFO('c', "Customer [%d] in group [%d] acquired lock [%d], owner %s", customerId, groupId, clerk->lock->getName(), clerk->lock->getOwnerThread()!=NULL?clerk->lock->getOwnerThread()->getName():"NULL") */;

    /* waiting line number minus 1     */
    subWaitingSize(clerk);

    if (!getIsBreak(clerk))
    {
        /* buyfood: interact with ConcessionClerk  */
        AcquireLock(clerk->lock);
        setIsBusy(clerk, true);
        ReleaseLock(lBuyFood);

        /*  tell clerk popcorn sum and soda sum */
        setGroupId(clerk, groupId);
        /* DEBUGINFO('c', "Customer [%d] in group [%d] interact with ConcessionClerk [%d]", customerId, groupId, lineIndex) */;
        for (i = 0; i < 2; ++i)
        {
            setFood(clerk, i, groupFoodSum[groupId][i]);
            SignalCV(clerk->lock, clerk->msgCondition);
            /*  wait for acknowledgement and amount */
            WaitCV(clerk->lock, clerk->msgCondition);
        }


        /* DEBUGINFO('c', "Customer [%d] in group [%d] is paying to ConcessionClerk [%d]", customerId, groupId, lineIndex) */;
        /*  give money */
        amount = getAmount(clerk);
        setPayment(clerk, amount);
        Printf("Customer [%d] in Group [%d] in ConcessionClerk line [%d] is paying [%d] for food\n",customerId,groupId,lineIndex,amount);
        SignalCV(clerk->lock, clerk->msgCondition);
        /* wait for clerk to acknowledge money  */
        WaitCV(clerk->lock, clerk->msgCondition);
        /*  customer leave, get next customer */
        SignalCV(clerk->lock, clerk->msgCondition);

        /* DEBUGINFO('c', "Customer [%d] in group [%d] is leaving ConcessionClerk [%d]", customerId, groupId, lineIndex) */;
        Printf("Customer [%d] in Group [%d] is leaving ConcessionClerk[%d]\n",customerId,groupId,lineIndex);
        ReleaseLock(clerk->lock);
    }
    else
    {
        /* if the clerk is on break,head customer need to start over and get another line */
        Printf("Customer [%d] in Group [%d] sees ConcessionClerk [%d] is on break.\n",customerId,groupId,lineIndex);
        /* DEBUGINFO('c', "Customer [%d] in group [%d] sees ConcessionClerk [%d] on break", customerId, groupId, lineIndex) */;
        /* subWaitingSize(clerk); */
        /*  for race condition of waiting size */
        ReleaseLock(lBuyFood);
        goto buyFood;
    }
}
/* Head customer go to ticket taker and check ticket */
void checkTickets(int customerId)
{
    int lineIndex;
    int i;
    int groupId;
    int amount;
    Customer  * currentCR;
    Employee * clerk;
    currentCR=&cr[customerId];
    groupId = currentCR->groupId;
checkTickets:
    lineIndex = -1;
    /* Head customer try to get in shortest line */
    while (lineIndex == -1)
    {
        lineIndex = getInLine(lCheckTickets, cNoTicketTaker, ttNumber, &noTicketTaker, tt, currentCR);
    }

    Printf("Customer [%d] in Group [%d] is getting in TicketTaker line [%d]\n",customerId,groupId,lineIndex);
    clerk = &tt[lineIndex];
    /* DEBUGINFO('c', "Customer [%d] in group [%d] get in %s [%d]", customerId, groupId, clerk->employeeType, lineIndex) */;

    /*  if the clerk is busy serving other customer,wait for the clerk to wake them up  */
    if (getIsBusy(clerk))
    {
        /* DEBUGINFO('c', "Customer [%d] in group [%d] wait for TicketTaker [%d]", customerId, groupId, lineIndex) */;
        WaitCV(lCheckTickets, clerk->lineCondition);

    }
    /* DEBUGINFO('c', "Customer [%d] in group [%d] acquire lTicketTaken with %s [%d], lTicketTaken's owner is: %s", customerId, groupId, clerk->employeeType, lineIndex, lTicketTaken->getOwnerThread()==NULL?"NULL":lTicketTaken->getOwnerThread()->getName()) */;
    /* ticket taken : interact with ticket taker  */
    /* waiting line number minus 1     */
    subWaitingSize(clerk);

    /* DEBUGINFO('c', "Customer [%d] in Group [%d] is walking up to %s [%d] to give [%d] tickets.\n",customerId,groupId, clerk->employeeType, lineIndex, ticketReceipt[groupId]) */;
    Printf("Customer [%d] in Group [%d] is walking up to TicketTaker[%d] to give [%d] tickets.\n",customerId,groupId,lineIndex, currentCR->groupSize);

    /*  ticker take: interact with TicketTaker  */
    if (!getIsBreak(clerk) )
    {
        AcquireLock(clerk->lock);
        /* DEBUGINFO('c', "Customer [%d] in group [%d] get lock with %s [%d]", customerId, groupId, clerk->employeeType, lineIndex) */;
        setIsBusy(clerk, true);
        /* DEBUGINFO('c', "Customer [%d] in group [%d] wait %s [%d]", customerId, groupId, clerk->employeeType, lineIndex) */;
        ReleaseLock(lCheckTickets);
        /* DEBUGINFO('c', "Customer [%d] in group [%d] wait %s [%d]", customerId, groupId, clerk->employeeType, lineIndex) */;
        setGroupId(clerk, groupId);
        /* DEBUGINFO('c', "Customer [%d] in group [%d] wait %s [%d]", customerId, groupId, clerk->employeeType, lineIndex) */;
        /*  give ticket taker ticket */
        setTicketSum(clerk, currentCR->groupSize);
        /* DEBUGINFO('c', "Customer [%d] in group [%d] wait %s [%d]", customerId, groupId, clerk->employeeType, lineIndex) */;
        SignalCV(clerk->lock, clerk->msgCondition);
        /* DEBUGINFO('c', "Customer [%d] in group [%d] wait %s [%d]", customerId, groupId, clerk->employeeType, lineIndex) */;
        /*  wait for ticket taker to check ticket */
        WaitCV(clerk->lock, clerk->msgCondition);
        /* DEBUGINFO('c', "Customer [%d] in group [%d] get lock with %s [%d]", customerId, groupId, clerk->employeeType, lineIndex) */;

        /*  if ticket taker foound the seats in the theater room are just not enough for the group, ticket taker stop take ticket */
        /*AcquireLock(lTicketTaken); */
        if (stopTicketTakenArr[lineIndex])
        {
            /*  ask all group to go back lobby and wait for recheck the tickets */
            ReleaseLock(clerk->lock);
            Printf("Customer [%d] in Group [%d] sees TicketTaker [%d] is no longer taking tickets. Going to the lobby2.\n", customerId, groupId, lineIndex);
            Printf("Customer [%d] in Group [%d] is in the lobby.\n", customerId, groupId);
            /* DEBUGINFO('c', "Customer [%d] in group [%d] wait to retake ticket", customerId, groupId, lineIndex) */;
            AcquireLock(lTicketTaken);
            if (stopTicketTaken)
            {
                WaitCV(lTicketTaken, cTicketTaken);
            }
            Printf("Customer [%d] in Group [%d] is leaving the lobby.\n",  customerId, groupId);
            /* DEBUGINFO('c', "Customer [%d] in group [%d] get lock with ticket taker [%d]", customerId, groupId, lineIndex) */;
            ReleaseLock(lTicketTaken);
            goto checkTickets;
        }
        /* DEBUGINFO('c', "Customer [%d] in group [%d] is leaving %s [%d] get into room", customerId, groupId, clerk->employeeType, lineIndex) */;
        /*  get into room */
        SignalCV(clerk->lock, clerk->msgCondition);
        Printf("Customer [%d] in Group [%d] is leaving TicketTaker[%d]\n",customerId,groupId,lineIndex);
        /* DEBUGINFO('c', "Customer [%d] in group [%d] is leaving %s [%d] after get into room", customerId, groupId, clerk->employeeType, lineIndex) */;
    }
    else
    {
        /* if the clerk is on break,head customer need to start over and get another line */
        /* DEBUGINFO('c', "Customer [%d] in group [%d] sees TicketTaker [%d] is on break.", customerId, groupId, lineIndex) */;
        Printf("Customer [%d] in Group [%d] sees TicketTaker [%d] is on break.\n",customerId,groupId,lineIndex);
        AcquireLock(lCheckTickets);
        /*  for race condition of waiting size */
        ReleaseLock(lCheckTickets);
        goto checkTickets;
    }
    /* DEBUGINFO('c', "Customer [%d] in group [%d] is leaving %s [%d] b get into room", customerId, groupId, clerk->employeeType, lineIndex) */;
    ReleaseLock(clerk->lock);
    /* DEBUGINFO('c', "Customer [%d] in group [%d] is leaving %s [%d] c get into room", customerId, groupId, clerk->employeeType, lineIndex) */;
}

/* Head customer find available seats */
void getSeats(int Size){
    
    int i=0,j=0;
    int consecutive=0,requestSeat=0;
    int seatAvailable[MAX_ROW]={0};

    int bFind=false;
    
    /* assumption:customer always choose seat from the beginning of a row */
     /* count available seats */
    for(i=0;i<MAX_ROW;i++){
        consecutive=0;
        for(j=0;j<MAX_COL;j++){
            if(seatState[j+i*MAX_COL]==false){
                consecutive++;         
            }
        }
        seatAvailable[i]=consecutive; 
    }


    /* Search for the first time if any consecutive seats */
    for(i=0;i<MAX_ROW;i++){  
        if(seatAvailable[i]>=Size){
            for(j=0;j<Size;j++){
                seatLocation[j]=(MAX_COL-seatAvailable[i]+j)+i*MAX_COL;               
                bFind=true;
            }
            seatAvailable[i]=seatAvailable[i]-Size;
            break;
        }
    }
    /* if there is no consecutive seats, choose available seats */
    if(bFind==false){
        for(i=0;i<MAX_ROW;i++){ 
            while(seatAvailable[i]!=0&& requestSeat<Size){
                seatLocation[requestSeat]=(MAX_COL-seatAvailable[i])+i*MAX_COL; 
                /* DEBUGINFO('c', "SeatAvailable RequestSeat[%d][%d][%d]",i, SeatAvailable[i],RequestSeat) */; 
                requestSeat++;   
                seatAvailable[i]--;     
              }
        }
            
    }
}
/*  Head customer get seat for the group */
void arrangeSeats(int customerId, int groupId) {
    /*  lock the seats and make sure no other head customer is going to arrange seats  */
    AcquireLock(lFindSeats);
    /* DEBUGINFO('c', "SeatAvailable group size[%d]\n",groupSize) */;  

    /* get seat number  */
    getSeats(cr[customerId].groupSize);
    seatPos = 0;	
    Printf("Customer [%d] in Group [%d] has found the following seat: row [%d] and seat [%d]\n",customerId,groupId,(seatLocation[seatPos]/MAX_ROW),seatLocation[seatPos]%MAX_COL);  
    seatState[seatLocation[seatPos]]=true;
    ++seatPos;
    AcquireLock(lGroup[groupId]);
    /* DEBUGINFO('c', "Customer [%d] in Group [%d] arrange seat %d", customerId,groupId,SeatLocation[0]) */;
    
    /*  tell other group member the seat number  */
    BroadcastCV(lGroup[groupId], cGroup[groupId]);
    groupSeat[groupId] = true;
    if (seatPos != cr[customerId].groupSize) {
    /* DEBUGINFO('c', "Customer [%d] in Group [%d] arrange seat %d", customerId,groupId,SeatLocation[i]) */;
        WaitCV(lGroup[groupId], cGroup[groupId]);
    }
    ReleaseLock(lGroup[groupId]);
    ReleaseLock(lFindSeats);      

}

/* customers wait fot movie to start */
void watchMovie(int customerId, int groupId){
    Employee * currentMT = &mt;
    AcquireLock(currentCustomerSeatedlock);
    ++currentCustomerSeated;
    /*Printf("++currentCustomerSeated %d , ticketTaken %d\n", currentCustomerSeated, ticketTaken); */
    AcquireLock(lStopMovie);
    ReleaseLock(currentCustomerSeatedlock);
    /* Customers are checkd by Movie Techinician if they were seated */
    /* DEBUGINFO('c', "Customer [%d] in group [%d] is sitting in a theater room seat.", customerId, groupId) */;
    Printf("Customer [%d] in group [%d] is sitting in a theater room seat.\n",customerId,groupId);
    /* watching movie and wait Movie Techinician to wake them up */
    WaitCV(lStopMovie,currentMT->msgCondition);
    ReleaseLock(lStopMovie);
    AcquireLock(currentCustomerSeatedlock);
    --currentCustomerSeated;
    if (!currentCustomerSeated) {
        SignalCV(currentCustomerSeatedlock, currentMT->lineCondition);
    }
    ReleaseLock(currentCustomerSeatedlock);
    /* DEBUGINFO('c', "Customer [%d] in group [%d] end acquire lStopMovie, lStopMovie's owner : %s", customerId,groupId, lStopMovie->getOwnerThread() == NULL? "NULL": lStopMovie->getOwnerThread()->getName()) */;
    /* DEBUGINFO('c', "Customer [%d] in group [%d] is watching movie and wait up by Movie Techinician.", customerId, groupId) */;
}

void proceed(int customerId, int groupId, int *flag)
{
    /* DEBUGINFO('c', "Customer [%d] in Group [%d] proceed to next.\n",customerId,groupId) */;
    /* DEBUGINFO('c', "Customer [%d] in Group [%d] proceed to next.\n",customerId,groupId) */;
    /*  tell group to go */
    /* DEBUGINFO('c', "Customer [%d] in Group [%d] acquire lGroup[%d], lGroup[]'s owner is %s ", customerId, groupId, groupId, lGroup[groupId]->getOwnerThread() == NULL? "NULL" : lGroup[groupId]->getOwnerThread()->getName()) */;
    AcquireLock(lGroup[groupId]);
    BroadcastCV(lGroup[groupId], cGroup[groupId]);
    Printf("HeadCustomer [%d] of group [%d] has told the group to proceed.\n", customerId, groupId);
    /*  monitor variables */
    flag[groupId] = true;
    ReleaseLock(lGroup[groupId]);
}



void headCustomer_start()
{
    int debug;
    int customerId, groupId;
    int i;
    Customer * current;
    int headcrNeedBathroom=false;
    AcquireLock(cutomerNoLock);
    for(i=0; i<numGroups; i++)
    {
        if(-1==headCustomerInGroup[i])
            break;
    }
    headCustomerInGroup[i]=nextCustomerId;
    current=&cr[nextCustomerId];
    customerId = nextCustomerId;
    current->customerId=nextCustomerId;
    groupId = i;
    current->groupId=i;
    current->groupSize=numCustomersInGroup[i];
    nextCustomerId++;
    Printf("customer %d is the head of group %d \n",current->customerId,current->groupId);
    ReleaseLock(cutomerNoLock);
    /*  ticket buyer */
    /* DEBUG('z', "\tGroup [%d] start buy tickets.\n", groupId) */;
    /* Head customer buy ticket for group member */
    buyTickets(customerId);
    /* DEBUG('z', "\tGroup [%d] finish buy tickets.\n", groupId) */;
    /* Head customer has buy the tickets and ask members to proceed to concession */
    proceed(customerId, groupId, groupTicket);
    /* DEBUGINFO('c', "Customer [%d] in group [%d] finish waitGroup after finish buy tickets", customerId, groupId); */

    /* Head customer ask group member if they want food. If no food requirement,go to ticket taker and check ticket */
    if (countFood(customerId, groupId))
    {
        /* DEBUGINFO('c', "Customer [%d] in group [%d] start buy food", customerId, groupId); */
        /* Head customer buy food for group member. */
        buyFood(customerId);
    }
    /* DEBUGINFO('c', "Customer [%d] in group [%d] finish buy food", customerId, groupId); */
    /* Head customer has buy food and ask members to proceed to check ticket */
    proceed(customerId, groupId, groupFood);
    /* DEBUGINFO('c', "Customer [%d] in group [%d] start check tickets.", customerId, groupId); */
    /* DEBUG('z', "\tGroup [%d] start \n", groupId) */;
    /* Head customer go to check ticket for group member */
    checkTickets(customerId);
    /* DEBUGINFO('c', "Customer [%d] in group [%d] finish check Ticket", customerId, groupId); */
    /* DEBUG('z', "\tGroup [%d] finish check tickets.\n", groupId) */;
    proceed(customerId, groupId, groupCheck);
    /* Head customer have ticket checked for group member and ask member to enter theater room */
    /* DEBUGINFO('c', "Customer [%d] in group [%d] start take seat", customerId, groupId); */
    /* DEBUG('z', "\tGroup [%d] start take seat.\n", groupId) */;
    /* Head customer find seat for group member */
    arrangeSeats(customerId, groupId);
    /* DEBUGINFO('c', "Customer [%d] in group [%d] finish take seat", customerId, groupId); */
    /* DEBUG('z', "\tGroup [%d] finish take seat.\n", groupId) */;
    /* Head customer watch movie */
    watchMovie(customerId, groupId);
    /* DEBUGINFO('c', "Customer [%d] in group [%d] finish watch Movie", customerId, groupId); */
    /* Head customer ask group member to leave theater room */
    proceed(customerId, groupId, groupLeaveRoom);
    /*    prtLeaveRoomMsg();*/
    Printf("Customer [%d] in group [%d] is getting out of a theater room seat.\n",customerId,groupId);
    checkBathroom(customerId, groupId);
    Printf("Customer [%d] in group [%d] bathroom over\n",customerId,groupId);
    proceed(customerId, groupId, groupLeaveTheater);
    Printf("Customer [%d] in group [%d] leave the theater\n",customerId,groupId);
    AcquireLock(lCustomerLeft);	
    customerLeft--; 
    ReleaseLock(lCustomerLeft);
    Exit(0);
}
/************************************************
 *
 *  end for head customer
 *
 ************************************************/
/************************************************
 *
 *  start for regular customer
 *
 ************************************************/

/*  regular customer wait for head customer to have ticket checked */
void waitTickets(int customerId, int groupId)
{
    AcquireLock(lGroup[groupId]);

    /* wait for head customer to tell them the tickets checked */
    /*  if head customer had broadcast, do not wait */
    Printf("Customer [%d] of group [%d] is waiting for the HeadCustomer.\n",customerId,groupId);
    if (!groupTicket[groupId])
    {
        WaitCV(lGroup[groupId], cGroup[groupId]);
    }
    /* tickets check and go to the next step */
    Printf("Customer [%d] of group [%d] has been told by the HeadCustomer to proceed.\n", customerId, groupId);
    ReleaseLock(lGroup[groupId]);
}

void waitFood(int customerId, int groupId)
{
    int i;
    AcquireLock(lGroup[groupId]);
    /*  wait for ticketbuyer to ask for if need food */
    if (!groupAskForFood[groupId])
    {
        WaitCV(lGroup[groupId], cGroupFood[groupId]);
    }
    /* random the food request */
    for (i = 0; i < 2; ++i)
    {
        groupFoodSum[groupId][i] += answerForFood(i);
    }
    ++answerFood[groupId];
    /*  tell head customer that they have chosen food */
    if (answerFood[groupId] == cr[customerId].groupSize)
    {
        SignalCV(lGroup[groupId], cGroupFood[groupId]);
    }
    /* wait for head customer to get the food */
    /*  if ticketbuyer had broadcast, do not wait */
    Printf("Customer [%d] of group [%d] is waiting for the HeadCustomer.\n",customerId,groupId);
    if (!groupFood[groupId])
    {
        WaitCV(lGroup[groupId], cGroup[groupId]);
    }
    /*  after get food, go to the next step */
    Printf("Customer [%d] of group [%d] has been told by the HeadCustomer to proceed.\n", customerId, groupId);
    ReleaseLock(lGroup[groupId]);
}
/* regular customer wait head customer to have the tickets check */
void waitCheck(int customerId, int groupId)
{

    /* DEBUGINFO('c', "Customer [%d] in group [%d] wait check a", customerId, groupId) */;

    AcquireLock(lGroup[groupId]);
    /* DEBUGINFO('c', "Customer [%d] in group [%d] wait check b", customerId, groupId) */;

    /* wait for the head customer to have ticket checked */
    Printf("Customer [%d] of group [%d] is waiting for the HeadCustomer.\n",customerId,groupId);
    if (!groupCheck[groupId]) {
        WaitCV(lGroup[groupId], cGroup[groupId]);
    }
    /* if ticket checked, go to the next step */
    /* DEBUGINFO('c', "Customer [%d] in group [%d] has been told by the HeadCustomer to proceed.", customerId, groupId) */;
    Printf("Customer [%d] of group [%d] has been told by the HeadCustomer to proceed.\n", customerId, groupId);

    ReleaseLock(lGroup[groupId]);

}

/* regular customer wait head customer to tell the seat number */
void waitSeats(int customerId, int groupId) {
    Customer  * currentCR;
    currentCR = & cr[customerId];
    /* wait for head to get seats */
    AcquireLock(lGroup[groupId]);
    if (!groupSeat[groupId]) {
        WaitCV(lGroup[groupId], cGroup[groupId]);
    }
    /*  got the seat number from head customer and then info head customer */
    Printf("Customer [%d] in Group [%d] has found the following seat: row [%d] and seat [%d]\n",customerId,groupId,(seatLocation[seatPos]/MAX_ROW),(seatLocation[seatPos]%MAX_COL));
    seatState[seatLocation[seatPos]]=true;
    ++seatPos;
    if (seatPos == currentCR->groupSize) {
        SignalCV(lGroup[groupId], cGroup[groupId]);
    }
    ReleaseLock(lGroup[groupId]);        
}
void waitLeavingmovieRoom(int customerId, int groupId) {
    AcquireLock(lGroup[groupId]);
    if (!groupLeaveRoom[groupId]) {
        WaitCV(lGroup[groupId], cGroup[groupId]);
    }
    ReleaseLock(lGroup[groupId]);       
}
void waitLeavingTheater(int customerId, int groupId) {
    AcquireLock(lGroup[groupId]);
    if (!groupLeaveTheater[groupId]) {
        WaitCV(lGroup[groupId], cGroup[groupId]);
    }
    ReleaseLock(lGroup[groupId]);       
}

int answerForBathroom() {
    int chance = rand()%100;
    if(chance<=25){
        return true;
    }
    return false;
}


void waitBathroom(int customerId, int groupId) {
    int needBathroom;
    AcquireLock(lGroup[groupId]);
    if (!groupAskForBathroom[groupId]) {
        WaitCV(lGroup[groupId], cGroupBathroom[groupId]);
    }  
    answerBathroom[groupId]++;
    needBathroom=answerForBathroom();
    if(needBathroom){
        /*if want go bathroom,the number of customer going bathroom plus 1*/
        numInGroupAskForBathroom[groupId]++;
        Printf("Customer [%d] in Group [%d] is going to the bathroom.\n",customerId,groupId);    
        if(answerBathroom[groupId]==(numCustomersInGroup[groupId])){
            SignalCV(lGroup[groupId], cGroup[groupId]);
        }  
        ReleaseLock(lGroup[groupId]);
        Yield();
         AcquireLock(lGroup[groupId]);
        numInGroupAskForBathroom[groupId]--;
        if(0==numInGroupAskForBathroom[groupId]){
            SignalCV(lGroup[groupId], cGroupBathroom[groupId]);
        }
        ReleaseLock( lGroup[groupId]); 
        Printf("Customer [%d] in Group [%d] is going out the bathroom.\n",customerId,groupId); 
    }
    else{
        Printf("Customer [%d] in Group [%d] do not go to the bathroom.\n",customerId,groupId); 
        if (answerBathroom[groupId] == cr[customerId].groupSize)
        {
            SignalCV(lGroup[groupId], cGroupBathroom[groupId]);
        }
        ReleaseLock(lGroup[groupId]);
    }
}

void checkBathroom(int customerId, int groupId) {

    AcquireLock(lGroup[groupId]);
    ++answerBathroom[groupId];
    /*  ask group mate if they want food */
    BroadcastCV(lGroup[groupId], cGroupBathroom[groupId]);
    /*  set monitor variable true */
    groupAskForBathroom[groupId] = true;
    /*  wait for all the customer answer for food */
    if (answerBathroom[groupId] != cr[customerId].groupSize)
    {
        WaitCV(lGroup[groupId], cGroupBathroom[groupId]);
    }
    ReleaseLock(lGroup[groupId]);
   /* check if head customer want to go to bathroom */
    if(answerForBathroom()){
        Printf("Customer [%d] in Group [%d] is going to the bathroom.\n",customerId,groupId);
        /*goBathroom(); */
        Printf("Customer [%d] in Group [%d] is leaving the bathroom.\n",customerId,groupId);
        Printf("Customer [%d] in Group [%d] is in the lobby.\n",customerId,groupId); 
    }else{
      	 Printf("Customer [%d] in Group [%d] is in the lobby.\n",customerId,groupId); 
    }
    AcquireLock( lGroup[groupId]);
    if(numInGroupAskForBathroom[groupId]!=0){
        WaitCV(lGroup[groupId], cGroupBathroom[groupId]);
    }
    ReleaseLock(lGroup[groupId]);
}


void regularCustomer_startWork()
{
    int debug;
    int customerId, groupId;
    int needBathroom;
    int i;
    Customer * current;
    AcquireLock(cutomerNoLock);
    customerId = nextCustomerId++;
    for(i = 0; i<numGroups; i++)
    {
        if(curCustomerNumInGroup[i]<(numCustomersInGroup[i]-1))
            break;
    }
    current=&cr[customerId];
    current->customerId=customerId;
    groupId = i;
    current->groupId=i;
    current->groupSize=numCustomersInGroup[i];
    curCustomerNumInGroup[i]++;
    ReleaseLock(cutomerNoLock);

    /* DEBUGINFO('c', "Customer [%d] in group [%d] waitTickets", customerId, groupId); */
    /* regular customer wait forhead customer to buy tickets */
    waitTickets(customerId, groupId);
    /* DEBUGINFO('c', "Customer [%d] in group [%d] waitFood", customerId, groupId); */
    /* regular customer wait forhead customer to buy Food     */
    waitFood(customerId, groupId);
    /* DEBUGINFO('c', "Customer [%d] in group [%d] waitCheck", customerId, groupId); */
    /* regular customer wait for head customer to check ticket     */
    waitCheck(customerId, groupId);
    /* DEBUGINFO('c', "Customer [%d] in group [%d] waitSeats", customerId, groupId);  */
    /* regular customer wait for head customer to arrange food */
    waitSeats(customerId, groupId);
        /* DEBUGINFO('c', "Customer [%d] in group [%d] watchMovie", customerId, groupId); */
    /* regular customer watch movie */
    watchMovie(customerId, groupId);
    /* DEBUGINFO('c', "Customer [%d] in group [%d] waitLeaveRoom", customerId, groupId); */
    /* regular customer wait for head customer to ask them to leave theater room together */
   waitLeavingmovieRoom(customerId, groupId);
   Printf("Customer [%d] in group [%d] is getting out of a theater room seat.\n",customerId,groupId);
        /* DEBUGINFO('c', "Customer [%d] in group [%d] waitBathroom", customerId, groupId); */
   waitBathroom( customerId,  groupId);
   Printf("Customer [%d] in group [%d] bathroom over\n",customerId,groupId);
   waitLeavingTheater(customerId, groupId);
   Printf("Customer [%d] in group [%d] leave the theater\n",customerId,groupId);
   AcquireLock(lCustomerLeft);	
    customerLeft--; 
    ReleaseLock(lCustomerLeft);
   Exit(0);
}
/************************************************
 *
 *  end for regular customer
 *
 ************************************************/

void divideIntoGroups( )
{
    int customersToAddToGroups = numCustomers;
    int groupSize = 0;
    while(customersToAddToGroups > 0)
    {
        if(customersToAddToGroups < 5) /*Don't add more customers to a group than remain free*/
            groupSize = 4%customersToAddToGroups + 1;
        else
            groupSize = 3%5 + 1; /*Max group size is 5*/
        customersToAddToGroups -= groupSize;
/*        Printf("customersToAddToGroups %d \n",customersToAddToGroups);*/
        numCustomersInGroup[numGroups] = groupSize;
/*        Printf("group [%d] has %d members %d\n",numGroups,groupSize, numCustomersInGroup[numGroups]);*/
        numGroups++;
    }
    numGroupsRemaining = numGroups;
/*    Printf("Number of Groups = [%d]\n", numGroups);
    Printf("Number of Customers = [%d]\n", numCustomers);*/
}

void generateCustomer( )
{
    int nextCustomerNumber=0;
    int i=0;
    int j=0;
    for (; i<numGroups; i++ )
    {
        /*Generate the size of the group, not counting head customer*/
        int groupSize = numCustomersInGroup[i];
        /*Create and fork the head customer thread*/
        /*The head customer of a group needs a group number, group size,*/
        /*and a customer number. I will store all 3 values in a struct pointer v and pass*/
        /*it as the second argument to Fork*/
        /* Printf( "we generate a group :size %d, number %d, headercustomer %d\n", current.groupSize,current.groupNumber,current.customerNumber);*/
        Fork(headCustomer_start);
        /*Now make all the regular Customers for this group*/

        for (j=0 ; j<(groupSize-1); j++ )
        {
            Fork(regularCustomer_startWork);
            /*Printf("Customer [%d] in Group [%d] has entered the movie theater\n", current.customerNumber, current.groupNumber);*/
        }
    }
}

/************************************************
 *
 *  end for customer
 *
 ************************************************/

/************************************************
 *
 *  start for ticketClerk
 *
 ************************************************/

/* the manager told the ticketclerk to work */


void ticketClerk_startWork()
{
    int i;
    int tcId;
    Employee* currentTC;
    int ticketSum;

    /* get curTickclerkno */
    AcquireLock(ticketClerkWorknoLock);
    tcId = ticketClerkWorkno++;
    currentTC=&tc[tcId];
    currentTC->id = tcId;
    Printf("ticket clerk %d is going to work\n",tcId);
    ReleaseLock(ticketClerkWorknoLock);

    while(1)
    {
        AcquireLock(lBuyTickets);
        if (currentTC->isBreak)
        {
            /*if clerk is on break,wait for manager signal*/
            Printf("ticketclerk [%d] is going on break.\n", currentTC->id);
            if (!breakTicketClerk) {
                WaitCV(lBuyTickets, cBreakTicketClerk);
            }
            breakTicketClerk = 0;
            Printf("ticketclerk [%d] is coming off break.\n", currentTC->id);
            currentTC->isBreak=false;
        }
        /* if have customer, then set busy and signal the customer, else set free*/
        if (currentTC->waitingSize> 0)
        {
            currentTC->isBusy=true;
            Printf("TicketClerk [%d] has a line length [%d] and is signaling a customer\n",currentTC->id,currentTC->waitingSize);
            /* currentTC->waitingSize--; */
            SignalCV(lBuyTickets, currentTC->lineCondition);
        }
        else
        {
            Printf("TicketClerk [%d] has no one in line. I am available for a customer.\n",currentTC->id);
            currentTC->isBusy=false;
        }

        /*sell ticket:interact with customer*/
        AcquireLock(currentTC->lock);
        /* on service*/
        ReleaseLock(lBuyTickets);
        /* wait customer to signal*/
        WaitCV(currentTC->lock,currentTC->msgCondition);
        /* if break*/
        AcquireLock(lBuyTickets);
        if (currentTC->isBreak && !currentTC->isBusy)
        {
            currentTC->isBusy=false;
            ReleaseLock(currentTC->lock);
            ReleaseLock(lBuyTickets);
            continue;
        }
        else
        {
            currentTC->isBusy=true;
        }
        ReleaseLock(lBuyTickets);

        /* get tickets sum*/
        ticketSum = currentTC->ticketSum;
        /* tell customers amount*/
        currentTC->amount=currentTC->ticketSum*PRICE_TICKET;
        Printf("TicketClerk [%d] has an order for [%d] and the cost is [%d].\n",currentTC->id,currentTC->ticketSum,currentTC->amount);
        SignalCV(currentTC->lock,currentTC->msgCondition);
        WaitCV(currentTC->lock,currentTC->msgCondition);
        /* cal amount*/
        AcquireLock(lAmount);
        ticketClerkAmount[tcId] += currentTC->payment;
        ReleaseLock(lAmount);
        /* get money, handout the tickets*/
        ticketReceipt[currentTC->groupId] = ticketSum;
        AcquireLock(lTicketSold);
        totalTicketSold += ticketSum;
        ReleaseLock(lTicketSold);
        SignalCV(currentTC->lock,currentTC->msgCondition);
        /* customer leave, get next customer*/
        WaitCV(currentTC->lock,currentTC->msgCondition);
        ReleaseLock(currentTC->lock);
    }

    Exit(0);
}

void generateTC()
{
    int i=0;
    for (i = 0; i<tcNumber; i++ )
    {
        /*Create and fork the ticketClerk*/
        Fork(ticketClerk_startWork);
    }

}
/************************************************
 *
 *  end for ticketClerk
 *
 ************************************************/
/************************************************
 *
 *  start for concessionClerk
 *
 ************************************************/
void concessionClerk_startWork()
{
    int i;
    int ccId;
    Employee* currentCC;

    /* get curTickclerkno */
    AcquireLock(concessionClerkWorknoLock);
    ccId = concessionClerkWorkNO++;
    currentCC=&cc[ccId];
    currentCC->id = ccId;
    Printf("concession clerk %d is going to work\n",ccId);
    ReleaseLock(concessionClerkWorknoLock);


    while(1)
    {
        AcquireLock(lBuyFood);
        if (getIsBreak(currentCC))
        {
            /*if clerk is on break,wait for manager signal*/
            Printf("concessionclerk [%d] is going on break.\n", currentCC->id);
            if (!breakConcessionClerk) {
                WaitCV(lBuyFood, cBreakConcessionClerk);
            }
            breakConcessionClerk = 0;
            Printf("concessionclerk [%d] is coming off break.\n", currentCC->id);
            currentCC->isBreak=false;
        }
        /* if have customer, then set busy and signal the customer, else set freewewesws*/
        if (currentCC->waitingSize > 0)
        {
            currentCC->isBusy=true;
            Printf("ConcessionClerk [%d] has a line length [%d] and is signaling a customer.\n",currentCC->id,currentCC->waitingSize);
            /*subWaitingSize();*/
            SignalCV(lBuyFood,currentCC->lineCondition);
        }
        else
        {
            Printf("ConcessionClerk [%d] has no one in line. I am available for a customer.\n",currentCC->id);
            currentCC->isBusy=false;
        }
        AcquireLock(currentCC->lock);
        /* on service*/
        ReleaseLock(lBuyFood);
        /* wait customer or manager to signal*/
        WaitCV(currentCC->lock,currentCC->msgCondition);
        /* if break         */
        AcquireLock(lBuyFood);
        if (getIsBreak(currentCC) && !getIsBusy(currentCC))
        {
            currentCC->isBusy=false;
            ReleaseLock(currentCC->lock);
            ReleaseLock(lBuyFood);
            continue;
        }
        else
        {
            currentCC->isBusy=true;
        }
        ReleaseLock(lBuyFood);
        SignalCV(currentCC->lock,currentCC->msgCondition);
        WaitCV(currentCC->lock,currentCC->msgCondition);
        /* tell customers food price */
        currentCC->amount = currentCC->food[0]*PRICE_POPCORN +currentCC->food[1]*PRICE_SODA;
        Printf("ConcessionClerk [%d] has an order for [%d] popcorn and [%d] soda. The cost is [%d].\n",currentCC->id,currentCC->food[0],currentCC->food[1],currentCC->amount);

        SignalCV(currentCC->lock,currentCC->msgCondition);
        /* wait for customer pay*/
        WaitCV(currentCC->lock,currentCC->msgCondition);
        /* amount*/
        AcquireLock(lAmount);
        concessionClerkAmount[currentCC->id] += currentCC->amount;
        ReleaseLock(lAmount);
        /* get money, handout the food*/
        SignalCV(currentCC->lock,currentCC->msgCondition);
        Printf("ConcessionClerk [%d] has been paid for the order.\n",currentCC->id);
        /* customer leave, get next customer*/
        WaitCV(currentCC->lock,currentCC->msgCondition);
        ReleaseLock(currentCC->lock);
    }
    Exit(0);
}
void generateCC()
{
    int i=0;
    for (i = 0; i<ccNumber; i++ )
    {
        /*Create and fork the ticketClerk*/
        Fork(concessionClerk_startWork);
    }

}
/************************************************
 *
 *  end for concession clerk
 *
 ************************************************/

/************************************************
 *
 *  start for ticket taker
 *
 ************************************************/

void tickettakerStartWork()
{
    int i;
    int ttId;
    Employee* currentTT;

    /* get curTickclerkno */
    AcquireLock(tickettakerWorknoLock);
    ttId = tickettakerWorkno++;
    currentTT=&tt[ttId];
    currentTT->id = ttId;
    Printf("tickettaker clerk %d is going to work\n",ttId);
    ReleaseLock(tickettakerWorknoLock);

    while(1)
    {
        /* stop ticket check*/
        /*        lCheckTickets AcquireLock();*/
        AcquireLock(lTicketTaken);
        if (stopTicketTaken == true)
        {
            AcquireLock(currentTT->lock);
            stopTicketTakenArr[currentTT->id] = true;
            ReleaseLock(currentTT->lock);
            ReleaseLock(lTicketTaken);
            AcquireLock(lCheckTickets);
            /* broadcast the waiting list*/
            /*BroadcastCV(lCheckTickets,currentTT->lineCondition);*/
            /*            setIsBusy=false;*/
            ReleaseLock(lCheckTickets);
            /* wait for manager to broadcast*/
            AcquireLock(lTicketTaken);
            WaitCV(lTicketTaken,cTicketTaken);
        }
        AcquireLock(currentTT->lock);
        stopTicketTakenArr[currentTT->id] = false;
        ReleaseLock(currentTT->lock);
        ReleaseLock(lTicketTaken);
        AcquireLock(lCheckTickets);
        if (currentTT->isBreak)
        {
            /* wait for manager signal*/
            Printf("tickettaker [%d] is going on break.\n", currentTT->id);
            if (!breakTicketTaker) {
                WaitCV(lCheckTickets, cBreakTicketTaker);
            }
            breakTicketTaker = 0;
            Printf("tickettaker [%d] is coming off break.\n", currentTT->id);
            currentTT->isBreak=false;

            /*            Printf("TicketTaker [%d] is going on break.\n",currentTT->id);*/
            /*            continue;*/
        }

        if (currentTT->waitingSize > 0)
        {
            Printf("TicketTaker [%d] has a line length [%d] and is signaling a customer.\n",currentTT->id,currentTT->waitingSize);
            currentTT->isBusy=true;
            SignalCV(lCheckTickets,currentTT->lineCondition);
        }
        else
        {
            Printf("TicketTaker [%d] has no one in line. I am available for a customer.\n",currentTT->id);
            currentTT->isBusy=false;
        }
        AcquireLock(currentTT->lock);
        /* on service*/
        ReleaseLock(lCheckTickets);
        /* wait customer to signal*/
        WaitCV(currentTT->lock,currentTT->msgCondition);
        /* if break ? race condition*/
        AcquireLock(lCheckTickets);
        if (currentTT->isBreak && !currentTT->isBusy)
        {
            currentTT->isBusy=false;
            ReleaseLock(currentTT->lock);
            ReleaseLock(lCheckTickets);
            continue;
        }
        else
        {
            currentTT->isBusy=true;
        }
        ReleaseLock(lCheckTickets);

        /*get tickets sum*/
        Printf("TicketTaker [%d] has received [%d] tickets.\n",currentTT->id, currentTT->ticketSum);
        /* if stopped*/
        ReleaseLock(currentTT->lock);
        AcquireLock(lTicketTaken);
        /*        if (stopTicketTaken) {*/
        /*            lTicketTaken ReleaseLock();*/
        /*            continue;*/
        /*        }*/
        /* if too much, has to stop*/
        /* ? if there is another ticketTaker on process, still not stop him*/
        AcquireLock(currentTT->lock);
        if (stopTicketTaken || ticketTaken + currentTT->ticketSum > 25)
        {
            stopTicketTaken = true;
            stopTicketTakenArr[currentTT->id] = true;
            Printf("TicketTaker [%d] is not allowing the group into the theater. The number of taken tickets is [%d] and the group size is [%d].\n", currentTT->id, ticketTaken, currentTT->ticketSum);
            /* signal the service one*/
            Printf("TicketTaker [%d] has stopped taking tickets.\n", currentTT->id);
            SignalCV(currentTT->lock, currentTT->msgCondition);
            ReleaseLock(currentTT->lock);
            ReleaseLock(lTicketTaken);
            /* broadcast the waiting list*/
            /*AcquireLock(lCheckTickets);
            BroadcastCV(lCheckTickets,currentTT->lineCondition);
            ReleaseLock(lCheckTickets);*/

            continue;
        }
        ticketTaken += currentTT->ticketSum;
        totalTicketTaken += currentTT->ticketSum;
        Printf("TicketTaker [%d] is allowing the group [%d] into the theater. The number of tickets taken is [%d].\n",currentTT->id, currentTT->groupId, ticketTaken);
        ReleaseLock(lTicketTaken);
        /* should just let in, not tell customers seats number*/
        /* better not to enforce seats number, which needs more variables to state*/
        SignalCV(currentTT->lock,currentTT->msgCondition);
        /* customer leave, get next customer*/
        WaitCV(currentTT->lock,currentTT->msgCondition);
        /* put lTicketTaken Release here to avoid race condition;*/
        ReleaseLock(currentTT->lock);
    }
    Exit(0);
}

void generateTT()
{
    int i=0;
    for (i = 0; i<ttNumber; i++ )
    {
        /*Create and fork the ticketClerk*/
        Fork(tickettakerStartWork);
    }
}

/************************************************
 *
 *  end for ticket taker
 *
 ************************************************/

/************************************************
 *
 *  start for movieTechnician
 *
 ************************************************/
void movieTechnicianStartWork(){
    Employee * currentMT;
    int i=0;
    currentMT=&mt;
    while(1)
    {
        /*step 1: to check the manager ask me to start or not, if not wait for*/
        AcquireLock(managerAskMTplaylock);
        if(false==managerAskMTPlay)
        {
            WaitCV(managerAskMTplaylock, mr.msgCondition);
        }
        managerAskMTPlay = false;
        ReleaseLock(managerAskMTplaylock);
        /*step 2: to check the seats is full or not*/
        /*AcquireLock(currentCustomerSeatedlock);
        if(!currentCustomerAllSeated) /*if not all the customer seated down, i need to wait here, customer seated add this number*/
        /*{
            WaitCV(currentCustomerSeatedlock, currentMT->lineCondition);
        }
        ReleaseLock(currentCustomerSeatedlock);*/
        /*step 3: play the movie*/
        Printf("The MovieTechnician has started the movie.\n");
        for (i = 0; i < 100; i++)
        {
            Yield();
        }
        Printf("The MovieTechnician has ended the movie.\n"); 
        /*step 4: movie over, notify the customer*/
        AcquireLock(lStopMovie);
        /*broadcast to notify all the customer to wake up, the movie is over*/
        BroadcastCV(lStopMovie, currentMT->msgCondition);
        Printf("The MovieTechnician has told all customers to leave the theater room.\n");
        ReleaseLock(lStopMovie);
        AcquireLock(currentCustomerSeatedlock);
        if (currentCustomerSeated) {
            WaitCV(currentCustomerSeatedlock, currentMT->lineCondition);
        }
        currentCustomerAllSeated = 0;
        ReleaseLock(currentCustomerSeatedlock);
        /*reset the seate state*/
        for(i=0;i< MAX_SEAT;i++){
            seatState[i]=false; /*reset seat state*/
        }

        /*step 5: movie over, notify the manager*/
        AcquireLock(lMovieState);
        movieState=0;    
        ReleaseLock(lMovieState);
    }
    Exit(0);
}

generateMT(){
    Fork(movieTechnicianStartWork);
}

/************************************************
 *
 *  end for movieTechnician
 *
 ************************************************/

/************************************************
 *
 *  start for the manager
 *
 ************************************************/
void randToBreak(int lockWaiting, Employee clerk[], int count, int *noClerk, int cNoClerk, int cBreak)
{
    int i = 0;
    Employee * current;
    /* lock to get in line*/
    AcquireLock(lockWaiting);
    for (i = 0; i < count; ++i)
    {
        current=&clerk[i];
        if (current->isBreak) continue;
        if (!current->waitingSize)
        {
            /* rand to break*/
            if (rand()%20 == 0)
            {
                current->isBreak=true;
                /* signal the waiting customer*/
                BroadcastCV(lockWaiting, current->lineCondition);
                if (!current->isBusy)
                {
                    AcquireLock(current->lock );
                    SignalCV(current->lock, current->msgCondition);
                    ReleaseLock(current->lock);
                }
                Printf("Manager has told %s [%d] to go on break.\n", current->employeeType, i);
            }
        }
        else if (current->waitingSize < 3)
        {
            int j = 0;
            for (j = 0; j < count; ++j)
            {
                if (i == j) continue;
                if (!clerk[j].isBreak)
                {
                    break;
                }
            }
          /*  if (j != count && rand()%5 == 0)*/
           if (j != count && rand()%20 == 0)
            {
                current->isBreak=true;
                /* signal the waiting customer*/
                BroadcastCV(lockWaiting,current->lineCondition);

                /* if not busy, manager should signal clerk to get break*/
                if (!current->isBusy)
                {
                    AcquireLock(current->lock);
                    SignalCV(current->lock,current->msgCondition);
                    ReleaseLock(current->lock);
                }
                Printf("Manager has told %s [%d] to go on break.\n", current->employeeType, i);
            }
        }
        else if (current->waitingSize> 5)
        {
            /*int j = 0;
            for (j = 0;j < count; ++j) {
                if (i == j) continue;
                if (clerk[j]->getIsBreak()) {
                    clerk[j]->setIsBreak(false);
                    break;
                }
            }*/
            SignalCV(lockWaiting, cBreak);

        }
    }
    /* if no clerk work and customers come, have to ask one to off break*/
    if (noClerk)
    {
        *noClerk = false;
        /* signal to get one clerk*/
        SignalCV(lockWaiting, cBreak);
        /* ? should get resp from clerk*/
        /* signal the customer*/
        BroadcastCV(lockWaiting,cNoClerk);
    }
    ReleaseLock(lockWaiting);
}
void managerStartWork()
{
    int i;
    while (1)
    {
        randToBreak(lBuyTickets, tc, tcNumber, &noTicketClerk, cNoTicketClerk, cBreakTicketClerk);
        randToBreak(lBuyFood, cc, ccNumber, &noConcessionClerk, cNoConcessionClerk, cBreakConcessionClerk);
        randToBreak(lCheckTickets, tt, ttNumber, &noTicketTaker, cNoTicketTaker, cBreakTicketTaker);
        /*to make sure whether to ask the ticket taker to take the tickets or not*/
        AcquireLock(lMovieState);
        if (movieState == 0)
        {
            AcquireLock(lTicketTaken);
            if (stopTicketTaken)
            {
                stopTicketTaken = false;
                /* set state to 1*/
                movieState = 1;
                ticketTaken = 0;
                /* ask customer and ticket clert to start ticket taken*/
                BroadcastCV(lTicketTaken,cTicketTaken);
            }
            ReleaseLock(lTicketTaken);
        }
        ReleaseLock(lMovieState);
        /*end start taken*/
        /*start a movie or not*/
        AcquireLock(lMovieState);
        if (movieState == 1)
        {
            AcquireLock(lTicketSold);
            AcquireLock(lTicketTaken);
            AcquireLock(currentCustomerSeatedlock);
            if ((stopTicketTaken || totalTicketTaken == totalTicketSold) && (ticketTaken == currentCustomerSeated && ticketTaken != 0))
            {
                stopTicketTaken = true;
                movieState = 2;
                AcquireLock(managerAskMTplaylock);
                managerAskMTPlay = true;
                SignalCV(managerAskMTplaylock, mr.msgCondition);
                ReleaseLock(managerAskMTplaylock);
            }
            ReleaseLock(currentCustomerSeatedlock);
            ReleaseLock(lTicketTaken);
            ReleaseLock(lTicketSold);
        }
        ReleaseLock(lMovieState);
        /**/
        for (i = 0; i < 1; ++i)
        {
            Yield();
        }
        /* if all customer leave, collect money*/
        AcquireLock(lCustomerLeft);
        if (customerLeft == 0)
        {
            /*collect money*/
            AcquireLock(lAmount);
            for (i = 0; i < tcNumber; ++i)
            {
                totalAmount += ticketClerkAmount[i];
                Printf("Manager collected [%d] from TicketClerk[%d].\n", ticketClerkAmount[i], i);
                ticketClerkAmount[i] = 0;
            }

            for (i = 0; i < ccNumber; ++i)
            {
                totalAmount += concessionClerkAmount[i];
                Printf("Manager collected [%d] from ConcessionClerk[%d].\n", concessionClerkAmount[i], i);
                concessionClerkAmount[i] = 0;
            }
            Printf("Total money made by office = [%d]\n", totalAmount);
            ReleaseLock(lAmount);
            AcquireLock(lTicketTaken);
            Printf("Total tickets sold by office = [%d]\n", totalTicketSold);
            ReleaseLock(lTicketTaken);
            AcquireLock(lTicketSold);
            Printf("Total tickets taken by office = [%d]\n", totalTicketTaken);
            ReleaseLock(lTicketSold);
            /**/
            ReleaseLock(lCustomerLeft);
            break;
        }
        ReleaseLock( lCustomerLeft);
    }
    Exit(0);
}
void generateMR() {
    Fork(managerStartWork); 
}

/************************************************
 *
 *  end for the manager
 *
 ************************************************/


void initTheater()
{
    int i;

    ticketClerkWorknoLock = createLock("ticketClerkWorknoLock");
    ticketClerkWorkno = 0;

    lCustomerLeft=createLock("lCustomerLeft");


    for (i = 0; i < MAX_GROUP; ++i)
    {
        headCustomerInGroup[i] = -1;
        lGroup[i] = createLock("lGroup");
        cGroup[i] = createCV("cGroup");
        cGroupFood[i] = createCV("cGroupFood");
        cGroupBathroom[i]=createCV("cGroupBath");
    }

    cutomerNoLock = createLock("cutomerNoLock");
    lBuyTickets = createLock("lBuyTickets");
    cNoTicketClerk = createCV("cNoTicketClerk");
    cBreakTicketClerk = createCV("cNoTicketClerk");
    cNoConcessionClerk = createCV("cNoConcessionClerk");
    cBreakConcessionClerk = createCV("cBreakConcessionClerk");
    cNoTicketTaker = createCV("cNoTicketTaker");
    cBreakTicketTaker = createCV("cBreakTicketTaker");

    noTicketClerk = 0;
    noConcessionClerk = 0;
    noTicketTaker = 0;

    totalTicketSold = 0;

    /*lock define*/
    lAmount = createLock("lAmount");
    lTicketSold = createLock("lTicketSold");
    /*end */
    for (i = 0; i < MAX_TC; ++i)
    {
        tc[i].lock = createLock("ticketClerkMsgLock");
        tc[i].lineCondition = createCV("ticketClerkLineCV");
        tc[i].msgCondition = createCV("msgCondition");
        /*Printf("tc[i].lock %d, tc[i].lineCondition %d, tc[i].msgCondition %d\n", tc[i].lock, tc[i].lineCondition, tc[i].msgCondition); */
        tc[i].ticketSum = 0;
        tc[i].isBusy = true;
        tc[i].isBreak = false;
        tc[i].waitingSize = 0;
        tc[i].amount = 0;
        tc[i].payment = 0;
        tc[i].employeeType="ticketclerk";
    }
    /*init for the concession clerk*/
    concessionClerkWorknoLock=createLock("concessionNOLock");
    lBuyFood=createLock("lBuyFoodLock");
    for (i = 0; i < MAX_CC; ++i)
    {
        cc[i].lock = createLock("concessionMsgLock");
        cc[i].lineCondition = createCV("concessionClerkLineCV");
        cc[i].msgCondition = createCV("msgCondition");
        cc[i].ticketSum = 0;
        cc[i].isBusy = true;
        cc[i].isBreak = false;
        cc[i].waitingSize = 0;
        cc[i].amount = 0;
        cc[i].payment = 0;
        cc[i].employeeType="concessionclerk";
    }
    /*init ticket taker*/
    lCheckTickets=createLock("lCheckTickets");
    cTicketTaken= createCV("cTicketTaken");
    lTicketTaken=createLock("lTicketTaken");
    tickettakerWorknoLock=createLock("tickettakerWorknoLock");
    for (i = 0; i < MAX_TT; ++i)
    {
        tt[i].lock = createLock("tickettakerLock");
        tt[i].lineCondition = createCV("tickettakerlineCV");
        tt[i].msgCondition = createCV("tickettakermsgCV");
        tt[i].ticketSum = 0;
        tt[i].isBusy = true;
        tt[i].isBreak = false;
        tt[i].waitingSize = 0;
        tt[i].amount = 0;
        tt[i].payment = 0;
        tt[i].employeeType="tickettaker";
    }
    lFindSeats = createLock("lFindSeats");
    /*int for the movie technician*/
    lStopMovie=createLock("lStopMovie");
    lMovieState=createLock("lMovieState");
    movieState = 1;

    managerAskMTplaylock=createLock("lMovieState");
    currentCustomerSeatedlock=createLock("lMovieState");
    mt.lock = createLock("tickettakerLock");
    mt.lineCondition = createCV("movieTechnicianLineCV");
    mt.msgCondition = createCV("movieTechnicianMsgCV");
    mt.ticketSum = 0;
    mt.isBusy = true;
    mt.isBreak = false;
    mt.waitingSize = 0;
    mt.amount = 0;
    mt.payment = 0;
    mt.employeeType="movie technician";


    mr.msgCondition = createCV("managerMsgCV");
    mr.employeeType="manager";


}

void main()
{
    int i=0;
    int inputValue = 0;

    while (inputValue < 1 || inputValue > 2) {
        Printf("Press 1 for normal simulation, 2 for default simulation:\n>");
        inputValue = getNumFromInput();
    }
    if (2 == inputValue) {
        numCustomers = 40;
        tcNumber = 5;
        ccNumber = 5;
        ttNumber = 3;
    } else if (1 == inputValue) {
        inputValue = 0;
        /* Allow user to decide number of entities */
        while(inputValue < 1 || inputValue > 40)
        {
            Printf("Enter number of customers (1 to 40):\n>");
            inputValue = getNumFromInput();
        }
        numCustomers = inputValue;
        inputValue = 0;
        while(inputValue < 1 || inputValue > 5)
        {
            Printf("Enter number of ticket clerks (1 to 5):\n>");
            inputValue = getNumFromInput();
        }
        tcNumber = inputValue;
        inputValue = 0;
        while(inputValue < 1 || inputValue > 5)
        {
            Printf("Enter number of concession clerks (1 to 5):\n>");
            inputValue = getNumFromInput();
        }
        ccNumber = inputValue;
        inputValue = 0;
        while(inputValue < 1 || inputValue > 3)
        {
            Printf("Enter number of ticket takers (1 to 3):\n>");
            inputValue = getNumFromInput();
        }
        ttNumber = inputValue;
    }
    customerLeft = numCustomers;


    initTheater();
    divideIntoGroups();
    Printf("Number of Customers = [%d]\n", numCustomers);
    Printf("Number of Groups = [%d]\n", numGroups);
    Printf("Number of TicketClerks = [%d]\n", tcNumber);
    Printf("Number of ConcessionClerks = [%d]\n", ccNumber);
    Printf("Number of TicketTakers = [%d]\n", ttNumber);
    generateCustomer();
    generateTC();
    generateCC();
    generateTT();
    generateMT();
    generateMR();
    Exit(0);

}

